package edu.hawaii.myisern.model;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.researchers.jaxb.Researcher;

/**
 * Does Sanity Check for Researchers, Organizations and Collaborations.
 * 
 * @author Ed Kim
 * @author Jon Lao
 * @author Jianfei Liao
 * @author Jared Sunouchi
 */
public class MyIsernSanityCheck {  
  
  /** A list with all researchers. */
  private List<Researcher> researchersList;
  /** A list with all organizations. */
  private List<Organization> organizationsList;
  /** A list with all collaborations. */
  private List<Collaboration> collaborationsList;
  /** A map that associates every researcher with an unique ID. */
  private Map<String, Researcher> researchersMap;
  /** A map that associates every organization with an unique ID. */
  private Map<String, Organization> organizationsMap;
  /** A string for " field is empty or invalid format. ". */
  private static final String INVALID_FIELD = " field is empty or invalid format. ";
  /** A string for " is not in the database. ". */
  private static final String INVALID_REFERENCE = " is not in the database. ";
  /** A string for " is not in proper range. ". */
  private static final String INVALID_YEAR = " is not in proper range (1990 to 2010). ";
  
  /**
   * Create a new instance of the checker.
   * 
   * @param model The MyISERN instance that will be checked.
   */
  public MyIsernSanityCheck(MyIsernModel model) {
    // get all researchers, organizations, and collaborations as lists
    this.researchersList = model.getResearchersList();
    this.organizationsList = model.getOrganizationsList();
    this.collaborationsList = model.getCollaborationsList();
    // get all researchers, organizations, and collaborations as maps
    this.researchersMap = model.getResearchersMap();
    this.organizationsMap = model.getOrganizationsMap();
    //this.collaborationsMap = model.getCollaborationsMap();
    
  }
  
  /**
   * Generates error list for invalid organization in researcher.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> invalidOrganizationInResearcher() {
    List<SanityError> errors = new ArrayList<SanityError>();
    
    for (Researcher r : this.researchersList) {
      String org = r.getOrganization();
      // if organization is not null and doesn't exist in the data map.
      if (org != null && !this.organizationsMap.containsKey(org.replace(' ', '_'))) {
        //errors.add(createErrorEntry(r.getName(), org, 1));
        errors.add(new SanityError(r.getName(), org, INVALID_REFERENCE));
      }          
    }    
    return errors;
  }
    
  /**
   * Generate error list for invalid affiliated researchers in organization.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> invalidAffiliatedResearchersInOrganization() {
    List<SanityError> errors = new ArrayList<SanityError>();
    for (Organization o : this.organizationsList) {
      List<String> aRList = o.getAffiliatedResearchers().getAffiliatedResearcher();
      for (String ar : aRList) {
        // if affiliated researcher is not null and doesn't exist in map.
        if (ar != null && !ar.equals("")
            && !this.researchersMap.containsKey(ar.replace(' ', '_'))) {
          errors.add(new SanityError(o.getName(), ar, INVALID_REFERENCE));
        }
      }
    }    
    return errors;
  }
  
  /**
   * Generate error list for invalid organization in collaboration.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> invalidOrganizationsInCollaboration() {
    List<SanityError> errors = new ArrayList<SanityError>();
    for (Collaboration c : this.collaborationsList) {
      List<String> oList = c.getCollaboratingOrganizations().getCollaboratingOrganization();
      for (String o : oList) {
        if (o != null && !this.organizationsMap.containsKey(o.replace(' ', '_'))) {
          errors.add(new SanityError(c.getName(), o, INVALID_REFERENCE));          
        }
      }
    }
    return errors;
  }
  
  /**
   * Generate error list for invalid year range in collaboration.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> invalidYearsInCollaboration() {
    List<SanityError> errors = new ArrayList<SanityError>();
    for (Collaboration c : this.collaborationsList) {
      List<BigInteger> yearList = c.getYears().getYear();
      for (BigInteger year : yearList) {
        if (year.intValue() < 1990 || year.intValue() > 2010) {          
          errors.add(new SanityError(c.getName(), year.toString(), INVALID_YEAR));
        }
      }
    }    
    return errors;
  }
  
  /**
   * Generate error list for empty organization in researcher.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> emptyOrganizationInResearcher() {
    List<SanityError> errors = new ArrayList<SanityError>();
    // this part actually can be combined with the invalid check part.
    for (Researcher r : this.researchersList) {
      if (r.getOrganization() == null || r.getOrganization().trim().isEmpty()) {
        errors.add(new SanityError(r.getName(), "Organization", INVALID_FIELD));        
      }
    }       
    return errors;
  }
      
  /**
   * Generates error list for empty affiliated researchers in organization.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> emptyAffiliatedResearchersInOrganization() {
    List<SanityError> errors = new ArrayList<SanityError>();
    // this part actually can be combined with the invalid check part.
    for (Organization o : this.organizationsList) {
      List<String> aRList = o.getAffiliatedResearchers().getAffiliatedResearcher();
      if (aRList == null || aRList.isEmpty() || aRList.get(0).equals("")) {
        errors.add(new SanityError(o.getName(), "Affiliated Researchers", INVALID_FIELD));        
      }
    }
    return errors;
  }
  
  /**
   * Generates error list for empty organization in collaboration.
   * 
   * @return list of the errors.
   */
  public synchronized List<SanityError> emptyOrganizationsInCollaboration() {
    List<SanityError> errors = new ArrayList<SanityError>();
    // this part actually can be combined with the invalid check part.
    for (Collaboration c : this.collaborationsList) {
      List<String> oList = c.getCollaboratingOrganizations().getCollaboratingOrganization();
      if (oList == null || oList.isEmpty() || oList.get(0).equals("")) {
        errors.add(new SanityError(c.getName(), "Collaborating Organizations", INVALID_FIELD));
      }
    }
    return errors;
  }
}